package funtools.groovy

import java.util.stream.Collectors

/**
 * @author LiangMu
 * @version \$Id: convertProperties2Yaml.java, v 0.1 2022年09月01日 5:35 下午 LiangMu Exp $$
 */
final class ConvertProperties2Yaml {
    private final static PROPERTIES_SUFFIX = '.properties'
    private final static YAML_SUFFIX = '.yaml'
    private final static TAB = ' ' * 4
    private final static ARRAY_VALUE_START_MARK = '  - '
    private final static ONE_SPACE = " "
    private static ConvertProperties2Yaml instance
    private Map<String, Config> configMap
    private String originalPath
    private String newPath
    private ConvertProperties2Yaml() {
    }

    static void main(String[] args) {
        def path = "/Users/liangyuanliu/Desktop/projects/leetcode/src/main/java/funtools/groovy/data/application.properties"
        convert(path)
    }

    private static void destroy() {
        instance = null
    }

    private static void construct(String path) {
        assert path.endsWith('.properties')
        instance = new ConvertProperties2Yaml()
        instance.originalPath = path
        def lastIndex = path.length() - PROPERTIES_SUFFIX.length()
        def newPath = path.substring(0, lastIndex) + YAML_SUFFIX
        instance.newPath = newPath
        instance.configMap = new HashMap<>()
    }

    static void convert(String path) throws Exception {
        def file = new File(path)
        convert(file)
    }

    static void convert(File properties) {
        if (!properties.exists() || !properties.isFile() || !properties.getName().endsWith('.properties')) {
            throw new Exception('the file not exist or the format error')
        }
        construct(properties.absolutePath)
        def scan = new Scanner(properties)
        try {
            while (scan.hasNextLine()) {
                def line = scan.nextLine()
                if (line.length() == 0 || line.startsWith('#')) continue
                analyze(line)
            }
            instance.configMap.each { sort(it.value) }
            createFile()
        } catch (Exception e) {
            e.printStackTrace()
        } finally {
            destroy()
            scan.close()
        }
    }

    private static void createFile() {
        def file = new File(instance.newPath)
        if (file.exists()) {
            throw new RuntimeException("the file have exist, file path = ${instance.newPath}")
        }
        try {
            file.createNewFile()
            def writer = new OutputStreamWriter(new FileOutputStream(file))
            def configList = instance.configMap.values()
            configList = configList.stream().sorted({ v1, v2 ->
                return v1.key <=> v2.key
            }).collect(Collectors.toList())
            configList.each { write(it, writer) }
            writer.close()
        } catch (Exception e) {
            file.delete()
            e.printStackTrace()
        }
    }

    private static void write(Config config, OutputStreamWriter writer) {
        write(config, writer, 0)
        writer.write('\n')
    }

    private static void write(Config config, OutputStreamWriter writer, Integer intervalLeft) {
        String val
        if (config.value != null && config.value.length() > 0) {
            val = parseValue(config.value, intervalLeft)
        } else {
            val = ''
        }
        String line = "${TAB * intervalLeft + config.key}:${val}\n"
        writer.write(line)
        intervalLeft += 1
        config.children.each { write(it, writer, intervalLeft) }
    }

    private static String parseValue(String value, Integer intervalLeft) {
        def valueArray = value.split(',')
        if (valueArray.length == 1) return "${ONE_SPACE}${valueArray[0]}"
        def start = TAB * intervalLeft + ARRAY_VALUE_START_MARK
        def result = new StringBuilder()
        valueArray.each {
            result.append("${start}${it}\n")
        }
        return "${ONE_SPACE}\n${result.toString()}"
    }

    private static void analyze(String text) {
        def map = text.split('=')
        if (map.size() != 2) return
        def configKey = map[0].trim()
        def configKeys = configKey.split('\\.').toList()
        final def key = configKeys[0]
        Config curConfig = Optional.ofNullable(instance.configMap.get(key)).orElseGet({
            def v = new Config()
            v.setKey(key)
            instance.configMap.put(key, v)
            return v
        })
        configKeys.remove(0)
        for (def item in configKeys) {
            def pattern = ~'\\S+\\[\\d+]'
            def lastIndex = item.findLastIndexOf { it == '[' }
            def b = item ==~ pattern
            def k = b ? item.substring(0, lastIndex) : item
            def temp = curConfig.getChildren().find { it.getKey() == k }
            if (temp == null) {
                def config = new Config()
                config.setKey(k)
                curConfig.getChildren().add(config)
                curConfig = config
            } else {
                curConfig = temp
            }
        }
        final def value = map[1].trim()
        curConfig.addValue(value)
    }

    private static void sort(Config config) {
        def children = config.getChildren()
        children.sort({ it.key })
        children.each { sort(it) }
    }

    private static class Config {
        String key
        String value
        List<Config> children

        Config() {
            this.children = new ArrayList<>()
        }

        void addValue(String value) {
            if (this.value != null && this.value.length() > 0) {
                this.value += ",${value}"
            } else {
                this.value = value
            }
        }
    }
}
